#!/usr/bin/python

from veil import extractARPDstMac
import socket, struct, sys, time, random, pickle, os
import traceback

# Local imports 
from veil import * # for the constants.
from threading import Thread
from mx.DateTime.DateTime import Friday
#from apport.hookutils import default

#######################################
#    Server Port THREAD FUNCTION
#######################################
def serverThread(server_socket):
    while(True):
        print '-----------------------------------------------------'
#        print myprintid, "---------------------------------- Count: " + str(count)
        client_socket, address = server_socket.accept()
        print "Received an incoming connection from ", address
        packet = receivePacket(client_socket) 
        client_socket.close()
        if len(packet) != 0:
            print 'Received packet: ', packet.encode("hex")
            if len(packet) < HEADER_LEN + 8:
                print 'Malformed packet! Packet Length:', len(packet), 'Expected:', HEADER_LEN + 8
                continue
            printPacket(packet, L)
            processPacket(packet)
        else:
            print '[FastRRDebug] NHA test received'
            client_socket.close()
###############################################
#    Server THREAD FUNCTION ENDS HERE
###############################################

#######################################
#    failureThread THREAD FUNCTION
#######################################
def failureThread(ftime):
    pid = os.getpid()
    print '[NodeFailure] Node:', myvid, 'will fail after', ftime, ' seconds and pid is ', pid
    time.sleep(ftime)
    print '[NodeFailure] Node:', myvid, 'is about to fail!!!!'
    #The README said there are exceptions that need to be handled, not sure what exceptions tho
    server_socket.close()
    os.system('kill -9 ' + str(pid))	
    print '[NodeFailure] Am I:', myvid, 'still alive?'
###############################################
#    failureThread THREAD FUNCTION ENDS HERE
###############################################

# Adds an entry to rdvStore, and also ensures that there are no duplicates
def addIfNODuplicateRDVENTRY(dist, newentry):
    global rdvStore
    for x in rdvStore[dist]:
        if x[0] == newentry[0] and x[1] == newentry[1]:
            return
    rdvStore[dist].append(newentry)
    
# Finds a logically closest gateway for a given svid    
def findAGW(rdvStore, k, svid):    
    gw = {}
    if k not in rdvStore:
        return ''
    for t in rdvStore[k]:
        r = delta(t[0], svid)
        if r not in gw:
            gw[r] = t[0]
        gw[r] = t[0]  
    if len(gw) == 0:
        return ''
    print "GATEWAYS FOUND: " + str(gw)
    s = gw.keys()
    s.sort()
    if(len(s) >= 3):
        return [gw[s[0]], gw[s[1]], gw[s[2]]]
    elif(len(s) >= 2):
        return [gw[s[0]], gw[s[1]]]
    else:
        return [gw[s[0]]]


def reduceTTL(packet):
    TTL = int(bin2str((struct.unpack("!B", packet[20:21]))[0], L), 2)
    if TTL <= 1:
        print myprintid, "################################################################### TTL EXPIRED!!" 
        return None
    addInPacketCount(TTL)
    TTL = TTL - 1
    print myprintid, "################################################################### NEW TTL: " + str(TTL)
    svid = bin2str((struct.unpack("!I", packet[8:12]))[0], L)
    dvid = bin2str((struct.unpack("!I", packet[12:16]))[0], L)
    fdvid = bin2str((struct.unpack("!I", packet[16:20]))[0], L)
    pkt = struct.pack('!HHBBH', HTYPE, PTYPE, HLEN, PLEN, 0x0000)\
            + struct.pack('!I', int(svid, 2))\
            + struct.pack('!I', int(dvid, 2))\
            + struct.pack('!I', int(fdvid, 2))\
            + struct.pack('!B', TTL)\
            + struct.pack('!BHI', 0x00, 0x0000, 0x00000000)
    return pkt
    
######################################
#    checkIfNhopAlive start here
######################################
def checkIfNhopAlive(nhop):
#    return 1
    toNhop = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ##socket.setdefaulttimeout(1.0)
    toNhop.settimeout(0.0001)
    address = nhop.split(':')
    ##toNhop.connect((address[0],int(address[1])))
    result = toNhop.connect_ex((address[0],int(address[1])))
#    toNhop.shutdown(socket.SHUT_RDWR)
    toNhop.close()
    if result == 0:
        print "[FastRRDebug]Next hop alive: TURE"
        return 1
    else:
        print "[FastRRDebug]Next hop alive: FALSE"
        return 0

######################################
#    checkIfNhopAlive ENDS HERE
######################################

def getFeasibleGW(excludeList, possibleGWs):
    feasibleGWs = []
    for gateway in possibleGWs:
        feasible = True
        for exclude in excludeList:
            if gateway[0] == exclude:
                feasible = False
        if feasible == True:
            feasibleGWs.append(gateway)
    if len(feasibleGWs) == 0:
        return None
    if len(feasibleGWs) > 1:
        gatewayToChoose = random.randint(0, len(feasibleGWs) - 1)
        return feasibleGWs[gatewayToChoose]
    return feasibleGWs[0]


######################################
#    handleNhopFailures STARTS HERE
######################################
def handleNhopFailures(src, dest, fdirective, packet, exclude, case):
    alternativeGW = None
    print myprintid, "############################################################# [FAILURE] HANDLING FAILURE, EXCLUDING :" + str(exclude)

    if case == 0:
        print myprintid, "############################################################# [FAILURE:0] DESTNATION " + dest + " IS MY PHYSICAL NEIGHBOR"
    
    if case == 1:
        print myprintid, "############################################################# [FAILURE:1] I AM THE FDIRECTIVE, LOOKING FOR ALTERNATIVE PATHS TO DESTINATION"
        dist = delta(myvid, dest)
        if dist in routingTable:
            possibleGWs = routingTable[dist]
            print myprintid, "############################################################# [FAILURE:1] POSSIBLE GATEWAYS: " + str(possibleGWs) 
            alternativeGW = getFeasibleGW(exclude, possibleGWs)
            if alternativeGW != None:
                print myprintid, "############################################################# [FAILURE:1] USING ALTERNATIVE GATEWAY: " + str(alternativeGW)
            else:
                print myprintid, "############################################################# [FAILURE:1] NO ALTERNATIVE FOUND"
    
    if case == 2:
        print myprintid, "############################################################# [FAILURE:2] FDIRECTIVE " + fdirective + " IS MY PHYSICAL NEIGHBOR, LOOKING FOR ALTERNATIVE PATHS TO DESTINATION"
        dist = delta(myvid, dest)
        if dist in routingTable:
            possibleGWs = routingTable[dist]
            print myprintid, "############################################################# [FAILURE:2] POSSIBLE GATEWAYS: " + str(possibleGWs)
            alternativeGW = getFeasibleGW(exclude, possibleGWs)
            if alternativeGW != None:
                print myprintid, "############################################################# [FAILURE:2] USING ALTERNATIVE GATEWAY: " + str(alternativeGW)
            else:
                print myprintid, "############################################################# [FAILURE:2] NO ALTERNATIVE FOUND"
    
    if case == 3:
        print myprintid, "############################################################# [FAILURE:3] LOOKING FOR ALTERNATIVE PATHS TO FDIRECTIVE"
        dist = delta(myvid, fdirective)
        if dist in routingTable:
            possibleGWs = routingTable[dist]
            print myprintid, "############################################################# [FAILURE:3] POSSIBLE GATEWAYS: " + str(possibleGWs)
            alternativeGW = getFeasibleGW(exclude, possibleGWs)
            if alternativeGW != None:
                print myprintid, "############################################################# [FAILURE:3] USING ALTERNATIVE GATEWAY: " + str(alternativeGW)
            else:
                print myprintid, "############################################################# [FAILURE:3] NO ALTERNATIVE FOUND, DEGENERATING TO CASE 2"
                dist = delta(myvid, dest)
                if dist in routingTable:
                    possibleGWs = routingTable[dist]
                    print myprintid, "############################################################# [FAILURE:2] POSSIBLE GATEWAYS: " + str(possibleGWs)
                    alternativeGW = getFeasibleGW(exclude, possibleGWs)
                    if alternativeGW != None:
                        print myprintid, "############################################################# [FAILURE:2] USING ALTERNATIVE GATEWAY: " + str(alternativeGW)
                    else:
                        print myprintid, "############################################################# [FAILURE:2] NO ALTERNATIVE FOUND"
    
    return alternativeGW
######################################
#    handleNhopFailures ENDS HERE
######################################        
                  

######################################
#    rmNhopFromAll start here
######################################
#If you pass a nhop that has already been deleted, you screwing up
#don't do it!
def rmNhopFromAll(nhop):
    global pid2vid_d, vid2pid_d, routingTable 	
    nhop_vid = pid2vid[nhop]
    print 'From rmNhopFromAll print DynamicCopy at', myvid, ':', pid2vid_d
    print 'From rmNhopFromAll print DynamicCopy at', myvid, ':', vid2pid_d
    #Deleting the neighbor info from the dynamnic dictionaries
    #and leaving the original unchanged, this is for the routeDataPacket function
    #to not break
    #try:
    del pid2vid_d[nhop]
    #except KeyError:
        #print 'KeyError exception happened at', myvid, ', nhop:', nhop, ';pid2vid_d:', pid2vid_d
    #try:
    del vid2pid_d[nhop_vid]
    #except KeyError:
        #print 'KeyError exception happened at',myvid,', nhop_vid:', nhop_vid, ';vid2pid_d:', vid2pid_d
    print '[FastRRDebug]To remove:', nhop_vid
    vlen = len(myvid)
    print '[FastRRDebug] Routing table before removing', nhop_vid
    printRoutingTable()
    
    for i in range(1, vlen + 1):
        if i in routingTable:
            routingTable[i] = [j for j in routingTable[i] if bin2str(j[0], L) != nhop_vid]
            if len(routingTable[i]) == 0:
                del routingTable[i]        
 
    print '[FastRRDebug] Removed all entries with', nhop_vid
    
    print '[FastRRDebug] New routing table looks like this'
    printRoutingTable()
        
######################################
#    rmNhopFromAll ENDS HERE
######################################

######################################
#    printRoutingTable start here
######################################

def printRoutingTable(): 
    vlen = len(myvid)
    print 'Printing Routing table at node:', myvid
    for i in range(1, vlen + 1):
        if i in routingTable:
            for j in routingTable[i]:
                print 'Bucket #', i, 'Next hop:', bin2str(j[0], L), 'Gateway:', bin2str(j[1], L), 'Prefix:', j[2], 'Default:', j[3]
        else:
            print 'Bucket #', i, '  --- E M P T Y --- ' 
        
######################################
#    printRoutingTable ENDS HERE
######################################

#######################################
#    PROCESSPACKET FUNCTION
#######################################
def processPacket(packet):
    dst = getDest(packet, L)
    packettype = getOperation(packet)
    
    # forward the packet if I am not the destination
    if dst != myvid:
        if packettype == DATA:
            packet = reduceTTL(packet)
            if packet == None:
                addDropPacket()
                return
        routepacket(packet)
        return
    
    # I am the destination of the packet, so process it.
    print myprintid, 'Processing packet'
    printPacket(packet, L)
    svid = bin2str((struct.unpack("!I", packet[8:12]))[0], L)
    payload = bin2str((struct.unpack("!I", packet[16:20]))[0], L)
    
    if packettype == DATA:
        print myprintid, "############################################## RECEIVED DATA PACKET!"
        addOutPacketCount()
        
    elif packettype == RDV_PUBLISH:
        dist = delta(myvid, payload)
        if dist not in rdvStore:
            rdvStore[dist] = []
        newentry = [svid, payload]
        addIfNODuplicateRDVENTRY(dist, newentry)
        return
        
    elif packettype == RDV_QUERY:
        k = int(payload, 2)
        # search in rdv store for the logically closest gateway to reach kth distance away neighbor
        gw = findAGW(rdvStore, k, svid)
        # if found then form the reply packet and send to svid
        if len(gw) == 0:
            # No gateway found
            print myprintid, 'No gateway found for the rdv_query packet to reach bucket: ', k, ' for node: ', svid
            return
        # create a RDV_REPLY packet and send it
        replypacket = createRDV_REPLY(gw, k, myvid, svid)
        addRDVPacketCount()
        #replypacket = createRDV_REPLY([int(gw,2), int(gw,2)],k,myvid, svid) # -Anshuman
        routepacket(replypacket)
        return
    elif packettype == RDV_REPLY:
        print "PACKET LENGTH: " + str(len(packet))
        # Fill my routing table using this new information
        gw = []
        if(len(packet) == 32):
            gw.append(struct.unpack("!I", packet[20:24]))
            gw.append(struct.unpack("!I", packet[24:28]))
            gw.append(struct.unpack("!I", packet[28:32]))
        if(len(packet) == 28):
            gw.append(struct.unpack("!I", packet[20:24]))
            gw.append(struct.unpack("!I", packet[24:28]))
        if(len(packet) == 24):
            gw.append(struct.unpack("!I", packet[20:24]))
        
        k = int(payload, 2)
        #The following 2 lines are moved to be contained with the nested if below
        #if k not in routingTable:
           #routingTable[k] = [] 
        for i in range(0, len(gw)):
            [tuplegw] = gw[i]
            gw_str = bin2str(tuplegw, L)
            if gw_str == myvid:
                #print '################################################################### PHYSICAL LINK ADVERTIZED AS GATEWAY:',gw_str
                continue
            nh = getNextHop(gw_str)
            if nh == '':
                print 'ERROR: no nexthop found for the gateway:', gw_str
                print 'New routing information couldnt be added! '
                return
            nexthop = int(pid2vid[nh], 2)
            bucket_info = [nexthop, tuplegw, getPrefix(myvid, k), "No"]
            if k not in routingTable:
                routingTable[k] = []
            if (len(routingTable[k])) < 3 and (not isDuplicateBucket(routingTable[k], bucket_info)): 
                routingTable[k].append(bucket_info)
                setDefault(routingTable[k])      
    else:
        print myprintid, 'Unexpected Packet!!'
###############################################
#    SPROCESSPACKET FUNCTION ENDS HERE
###############################################

#def getCurrentDefault(gateways):
#    defaultGateway = None
#    for gateway in gateways:
#        if gateway[3] == "Yes":
#            defaultGateway = gateway
#            break
#    return defaultGateway

def setDefault(gateways):
    defaultGateway = None
    lowestDist = 999
    for gateway in gateways:
        gateway[3] = "No"
        dist = delta(myvid, bin2str(gateway[1], L))
        if(dist < lowestDist):
            defaultGateway = gateway
            lowestDist = dist
    if defaultGateway != None:
        defaultGateway[3] = "Yes"
###############################################
#    getNextHop function starts here
###############################################

def getNextHop(destvid_str):
    nexthop = ''
    if destvid_str in vid2pid:
        return vid2pid[destvid_str]
    dist = delta(myvid, destvid_str)
    if dist in routingTable:
        nexthop = bin2str(routingTable[dist][0][0], L)
        nexthop = vid2pid[nexthop]
    return nexthop
    
###############################################
#    getNextHop FUNCTION ENDS HERE
###############################################

###############################################
#    sendPacket function starts here
###############################################

def sendPacket(packet,nexthop):
    # connect to the nexthop
    try:
        toSwitch = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        toSwitch.settimeout(0.01)
        address = nexthop.split(':')
        toSwitch.connect((address[0],int(address[1])))
        toSwitch.send(packet)
        toSwitch.close()
    except:
        print myprintid, "Unexpected Exception: ", 'received while sending packet to Switch at IP: ', address[0], 'port: ', address[1]
        exc_type, exc_value, exc_traceback = sys.exc_info()
        lines = traceback.format_exception(exc_type, exc_value, exc_traceback)
        print ''.join('!! ' + line for line in lines)
        toSwitch.close()
        printPacket(packet,L)
    
    # close the connection
    
###############################################
#    sendPacket FUNCTION ENDS HERE
###############################################

def routeControlPacket(packet):
    global myvid, routingTable, vid2pid, myprintid, L
    src = getSource(packet, L)
    nexthop = None
    
    # get the destination
    dst = getDest(packet, L)
    # If it's me
    if dst == myvid:
#        print myprintid, "################################################################### [CONTROL] I AM THE DESTINATION"
        processPacket(packet)
        return
    
    #If it's my physical Neighbor
    if dst in vid2pid:
#        print myprintid, "################################################################### [CONTROL] PACKET IS DESTINED TO MY PHYSICAL NEIGHBOR!"
        nexthop = vid2pid[dst]
#        print myprintid, "################################################################### [CONTROL] SENDING PACKET TO", nexthop
        sendPacket(packet, nexthop)
        return
     
    packettype = getOperation(packet)
    while nexthop == None:
        if dst in vid2pid:
            nexthop = vid2pid[dst]
            break

        dist = delta(myvid, dst)
        if dist == 0:
            break
        if dist in routingTable:
            nexthop = bin2str(routingTable[dist][0][0], L)
            nexthop = vid2pid[nexthop]
            break
        if (packettype != RDV_PUBLISH) and (packettype != RDV_QUERY):
            break 
        
        print myprintid, 'No next hop for destination: ', dst, ' dist: ', dist
        # flip the dist bit to
        print 'Changing dest by fliping bit'
        dst = flipBit(dst, dist)
         
    # Check of the destination has changed and update
    if dst != getDest(packet, L):
        packet = updateDestination(packet, dst)
    # Am I the destination now?
    if dst == myvid:
#        print myprintid, "################################################################### [CONTROL] I AM THE DESTINATION"
        printPacket(packet, L)
        processPacket(packet)
    # No nexthop was found -- will this case ever be hit?
    if nexthop == None:
        print myprintid, 'no route to destination' , 'MyVID: ', myvid, 'DEST: ', dst
        printPacket(packet, L)
        print myprintid, "+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_ [CONTROL] PACKET DROPPED! +_+_+_+_+_+_+_+_+_+_+_+_+_+_+_"
        return
     
#    print myprintid, "################################################################### [CONTROL] SENDING PACKET TO", nexthop
    sendPacket(packet, nexthop)


def routeDataPacket(packet):
    global myvid, routingTable, vid2pid, myprintid, L
    
    # case for failure handling
    case = -1
    #get source
    src = getSource(packet, L)
    #get destination
    dst = getDest(packet, L)
    print myprintid, "################################################################### DESTINATION: " + dst
    #get forwarding directive
    fdirective = getForwardingDirective(packet, L)
    print myprintid, "################################################################### FORWARDING DIRECTIVE: " + fdirective
    
    # If I'm the destination
    if dst == myvid:
        print myprintid, "################################################################### I AM THE DESTINATION"
        processPacket(packet)
        return
    
    nexthop = None
    # If the destination is my physical neighbor
    if dst in vid2pid:
        print myprintid, "################################################################### DESTINATION " + dst + " IS MY PHYSICAL NEIGHBOR"
        nexthop = vid2pid[dst]
        case = 0

    # If I am the forwarding directive
    elif fdirective == myvid:
        print myprintid, "################################################################### I AM THE FORWARDING DIRECTIVE"
        print myprintid, "################################################################### CHANGING THE FORWARDING DIRECTIVE TO: " + str(dst)
        packet = setForwardingDirective(dst, packet)
        # return a gateway which is closest to destination
        nexthop = selectNLH(dst, src)
        case = 1
        
    # If the forwarding directive is my physical neighbor
    elif fdirective in vid2pid:
        print myprintid, "################################################################### FORWARDING DIRECTIVE " + fdirective + " IS MY PHYSICAL NEIGHBOR"
        nexthop = vid2pid[fdirective]
        case = 2

    else:
        # If the forwarding directive is far away
        dist = delta(myvid, fdirective)
        if dist in routingTable:
            print myprintid, "################################################################### DISTANCE TO FORWARDING DIRECTIVE: " + str(dist)
            possibleGWs = routingTable[dist]
            print myprintid, "################################################################### POSSIBLE GATEWAYS: " + str(possibleGWs)
            #check if forwarding directive and destination are the same
            if fdirective == dst:
                print myprintid, "################################################################### FVID AND DVID ARE THE SAME"
                if len(possibleGWs) > 1:
                    chosenGW = chooseRandomGateway(dst, possibleGWs)
                else:
                    chosenGW = possibleGWs[0]
                fdirective = bin2str(chosenGW[1], L)
                print myprintid, "################################################################### SETTING FVID TO: " + fdirective
                
                if fdirective == myvid:
                    print myprintid, "################################################################### NOW I AM THE FORWARDING DIRECTIVE"
                    print myprintid, "################################################################### CHANGING THE FORWARDING DIRECTIVE TO: " + str(dst)
                    packet = setForwardingDirective(dst, packet)
                    nexthop = selectNLH(dst, src)
                    case = 1
                else:
                    packet = setForwardingDirective(fdirective, packet)
                    nexthop = vid2pid[bin2str(chosenGW[0], L)]
                    if fdirective in vid2pid:
                        print myprintid, "################################################################### FORWARDING DIRECTIVE " + fdirective + " IS MY PHYSICAL NEIGHBOR"
                        case = 2
                    else:
                        case = 3
            else:
                print myprintid, "################################################################### SIMPLY FORWARDING, LOOKING FOR A CLOSEST GATEWAY"
                closestGW = getClosestGateway(fdirective, possibleGWs)
                print myprintid, "################################################################### USING GATEWAY: " + str(closestGW[1])
                nexthop = vid2pid[bin2str(closestGW[0], L)]
                case = 3
                
    if nexthop == None:
        print myprintid, "################################################################### NO NEXTHOP FOR DESTINATION: ", fdirective, " AT DIST: ", dist
        addDropPacket()
        return
        
    print myprintid, "################################################################### FOUND NEXTHOP: " + nexthop
    if checkIfNhopAlive(nexthop) == 1:
        sendPacket(packet, nexthop)
        print myprintid, "################################################################### PACKET SENT TO", nexthop
        return
    else:
        nh = int(pid2vid[nexthop], 2)
        excludeList = []
        excludeList.append(nh)
        while True:
            alternativeGW = handleNhopFailures(src, dst, fdirective, packet, excludeList, case)
            if alternativeGW == None:
                print myprintid, "################################################################### COULD NOT FIND ALTERNATE PATH, DROPPING THE PACKET!!!"
                break
            else:
                nexthop = vid2pid[bin2str(alternativeGW[0], L)]
                print myprintid, "################################################################### USING ALTERNATE NEXTHOP: " + nexthop
                if checkIfNhopAlive(nexthop) == 1:
                    sendPacket(packet, nexthop)
                    print myprintid, "################################################################### PACKET SENT TO: ", nexthop
                    return
                else:
                    nh = int(pid2vid[nexthop], 2)
                    excludeList.append(nh)
        addDropPacket()
        
#            nexthop = handlerReturn[0]
#            packet = handlerReturn[1]
#            if nexthop != None:
#                print myprintid, "################################################################### SET NEW FORWARDING DIRECTIVE TO", getForwardingDirective(packet, L)
#                sendPacket(packet, nexthop)
#                print myprintid, "################################################################### PACKET SENT TO", nexthop
#                printPacket(packet, L)
#                return
#            #print "######################## PACKET DROPPED, OUT OF GATEWAYS GOING TO BUCKET#:",delta(myvid,dst), "################################"
#            addDropPacket()
    
    
    
###############################################
#    routepacket function starts here
###############################################

def chooseRandomGateway(dst, gateways):
    gatewayToChoose = random.randint(0, len(gateways) - 1)
    return gateways[gatewayToChoose]

def getClosestGateway(fvid, gateways):
    nextGateway = None
    lowestDist = 999
    for gateway in gateways:
        dist = delta(fvid, bin2str(gateway[1], L))
        if(dist < lowestDist):
            nextGateway = gateway
            lowestDist = dist
    return nextGateway
            
def selectNLH(dst, src):
    possibleGWs = {}
    #find k level gateway in vid2pid table
    nextHop = None
    lowestDist = 999
    for vid in vid2pid_d:
        if vid == src:
            continue
        dist = delta(dst, vid)
        if(dist < lowestDist):
            nextHop = vid2pid_d[vid]
            lowestDist = dist
    return nextHop

def setForwardingDirective(forwardingDirective, packet):
    TTL = int(bin2str((struct.unpack("!B", packet[20:21]))[0], L), 2)
    svid = bin2str((struct.unpack("!I", packet[8:12]))[0], L)
    dvid = bin2str((struct.unpack("!I", packet[12:16]))[0], L)
    fdvid = forwardingDirective
    pkt = struct.pack('!HHBBH', HTYPE, PTYPE, HLEN, PLEN, 0x0000)\
            + struct.pack('!I', int(svid, 2))\
            + struct.pack('!I', int(dvid, 2))\
            + struct.pack('!I', int(fdvid, 2))\
            + struct.pack('!B', TTL)\
            + struct.pack('!BHI', 0x00, 0x0000, 0x00000000)
    return pkt


def routepacket(packet):
    packettype = getOperation(packet)
    if (packettype == DATA):
        routeDataPacket(packet)
        return
    routeControlPacket(packet)

###############################################
#    routepacket FUNCTION ENDS HERE
###############################################
###############################################
#    Publish function starts here
###############################################

def publish(bucket, k):
    global myvid
    dst = getRendezvousID(k, myvid)
    packet = createRDV_PUBLISH(bucket, myvid, dst)
    addRDVPacketCount()
    print myprintid, 'Publishing my neighbor', bin2str(bucket[0], L), 'to rdv:', dst
    printPacket(packet, L)
    routepacket(packet)

###############################################
#    Publish FUNCTION ENDS HERE
###############################################

###############################################
#    Query function starts here
###############################################

def query(k):
    global myvid
    dst = getRendezvousID(k, myvid)
    packet = createRDV_QUERY(k, myvid, dst)
    addRDVPacketCount()
    print myprintid, 'Quering to reach Bucket:', k, 'to rdv:', dst
    printPacket(packet, L)
    routepacket(packet)

###############################################
#    Query FUNCTION ENDS HERE
###############################################

###############################################
#    Utility functions for perf. analysis
###############################################

def addInPacketCount(TTL):
    global perf, myvid
    addPacketCount()
    if TTL == 64:
        perf[INPACKETS] += 1 
    #    print "###################################################### PACKET ENTRY POINT"
    #    print "###################################################### PACKET NO AT NODE:" + str(myvid) + " = " + str(perf[INPACKETS])
	FILE = open(PERF_DIR + "output." + str(myvid) + ".p", "wb")
        pickle.dump(perf, FILE)
	FILE.close()


def addOutPacketCount():
    global perf, myvid
    addPacketCount()
    perf[OUTPACKETS] += 1 
    #print "###################################################### PACKET END POINT"
    #print "###################################################### PACKET NO AT NODE:" + str(myvid) + " = " + str(perf[OUTPACKETS])
    FILE = open(PERF_DIR + "output." + str(myvid) + ".p", "wb")
    pickle.dump(perf, FILE)
    FILE.close()

def addPacketCount():
    global perf
    perf[PACKETS] += 1
    #print "###################################################### PACKET ROUTE POINT"
    #print "###################################################### PACKET NO AT NODE:" + str(myvid) + " = " + str(perf[PACKETS])
    FILE = open(PERF_DIR + "output." + str(myvid) + ".p", "wb")
    pickle.dump(perf, FILE)
    FILE.close()

def addRDVPacketCount():
    global perf
    perf[RDVPACKETS] += 1
    #print myprintid, "###################################################### RDV PACKET SENT"
    #print myprintid, "###################################################### PACKET NO AT NODE:" + str(myvid) + " = " + str(perf[RDVPACKETS])
    FILE = open(PERF_DIR + "output." + str(myvid) + ".p", "wb")
    pickle.dump(perf, FILE)
    FILE.close()

def addDropPacket():
    global perf
    perf[DROPPACKETS] += 1
    #print "###################################################### PACKET DROPPED"
    print "###################################################### PACKET NO AT NODE:" + str(myvid) + " = " + str(perf[DROPPACKETS])
    FILE = open(PERF_DIR + "output." + str(myvid) + ".p", "wb")
    pickle.dump(perf, FILE)
    FILE.close()
    

###############################################
#    RunARount function starts here
###############################################

def runARound(round):
    global routingTable
    global vid2pid, pid2vid, mypid, myvid, L
    # start from round 2 since connectivity in round 1 is already learnt using the physical neighbors
    for i in range(2, round + 1):
        # see if routing entry for this round is already available in the routing table.
        if i in routingTable:
            if len(routingTable[i]) > 0:
                #publish the information if it is already there
                for t in routingTable[i]:
                    if t[1] == int(myvid, 2):
                        publish(t, i)
            else:
                query(i)
   #     else:  - Anshuman
        query(i)

###############################################
#    RunARound FUNCTION ENDS HERE
###############################################

if len(sys.argv) != 5:
    print '-----------------------------------------------'
    print 'Wrong number of input parameters'
    print 'Usage: ', sys.argv[0], ' <TopologyFile>', '<vid_file>', '<my_ip:my_port>', '<fail time in seconds>'
    print 'A node will have two identifiers'
    print 'i) pid: in this project pid is mapped to IP:Port of the host so if a veil_switch is running at flute.cs.umn.edu at port 5211 than pid of this switch is = flute.cs.umn.edu:5211'
    print 'ii) vid: It is the virtual id of the switch.'
    print 'TopologyFile: It contains the adjacency list using the pids. So each line contains more than one pid(s), it is interepreted as physical neighbors of the first pid.'
    print 'vid_file: It contains the pid to vid mapping, each line here contains a two tuples (space separated) first tuple is the pid and second tuple is the corresponding vid'
    print '-----------------------------------------------\n\n\n'
    sys.exit(0)

sleeptime = random.random()*5
print 'Sleeping :', sleeptime, ' seconds!'
time.sleep(sleeptime)
topofile = sys.argv[1]
vidfile = sys.argv[2]
myport = int((sys.argv[3].split(":"))[1])
mypid = sys.argv[3]
failtime = int(sys.argv[4])


# Learn my neighbors by reading the input adjacency list file
myneighbors = []
myvid = ''
pid2vid = {}
vid2pid = {}
#The _d vars hold the dynamnically changing neighbor information
#based on if the neighbor is down, we make changes to this dictionary
#and leave the original neighbor information in pid2vid and vid2pid resp.
pid2vid_d = {}
vid2pid_d = {}
routingTable = {} 
rdvStore = {} 
myprintid = ''
perf = {INPACKETS: 0, OUTPACKETS: 0, PACKETS: 0, RDVPACKETS: 0, DROPPACKETS: 0}
L = 0
default = ''
# Routing table is a dictionary, it contains the values at each distances from 1 to L
# So key in the routing table is the bucket distance, value is the 3 tuple: tuple 1 = nexthop (vid), tuple 2 = gateway (vid), tuple 3 = prefix (string)

# RDV STORE is a dictionary: it stores the list of edges with distances
# open the topo file
fin = open(topofile, 'r')
# read the file line by line
line = fin.readline()
line = line.strip()
while line != '':
    if line.find(mypid) == 0:
        # this is the line which contains the neighbor list for my 
        myneighbors = (line.split(' '))[1:]
        break
    line = fin.readline()
    line = line.strip()
fin.close()

if ' ' in myneighbors:
    print 'Warning: My neighbor list contains empty pids!'
if  len(myneighbors) < 1:
    print 'Warning: My neighbor list is empty, will quit now!'
    sys.exit(0)
print 'Myneighbors: ', myneighbors    
# Learn my and myneighbor's vids
fin = open(vidfile, 'r')
line = fin.readline()
line = line.strip()
while line != '':
    tokens = line.split(' ')
    if tokens[0] in myneighbors:
        pid2vid[tokens[0]] = tokens[1]
        vid2pid[tokens[1]] = tokens[0]  
    elif tokens[0] == mypid:
        myvid = tokens[1]
    
    line = fin.readline()
    line = line.strip()
fin.close()
pid2vid_d = pid2vid.copy()
vid2pid_d = vid2pid.copy()

# Learn L, it is the length of any vid
L = len(myvid)
myprintid = "VEIL_SWITCH: [" + mypid + '|' + myvid + ']'
# Now start my serversocket to listen to the incoming packets         
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#server_socket.settimeout(0.01)
server_socket.bind(("", myport))
server_socket.listen(5)
print myprintid, ' is now listening at port: ', myport

connections = {}
client_socket = None
# create a server thread to take care of incoming messages:
server = Thread(target=serverThread, args=([server_socket]))
server.start()

# initialize perf file with empty values
#pickle.dump(perf , open( PERF_DIR + "output." +str(myvid) + ".p", "wb" ) )

# if user entered non-zero value for fail time, then kill node after 'failtime' seconds
if failtime > 0:
    failure = Thread(target=failureThread, args=([failtime]))
    failure.start()

round = 1
# Put my physical neighnor information in the routing table in the round 1
for vid in vid2pid:
    dist = delta(vid, myvid)
    if dist not in routingTable:
        routingTable[dist] = []
        default = "Yes"
    bucket_len = len(routingTable[dist])
    bucket_info = [int(vid, 2), int(myvid, 2), getPrefix(myvid, dist), default]
    if not isDuplicateBucket(routingTable[dist], bucket_info):
        routingTable[dist].append(bucket_info)
        
while True:
    print myprintid, 'Starting Round #', round
    runARound(round)
    round = round + 1
    if round > L:
        round = L
    print '\n\t----> Routing Table at :', myvid, '|', mypid, ' <----'
    for i in range(1, L + 1):
        if i in routingTable:
            for j in routingTable[i]:
                print 'Bucket #', i, 'Nexthop:', bin2str(j[0], L), 'Gateway:', bin2str(j[1], L), 'Prefix:', j[2], 'Default:', j[3]
        else:
            print 'Bucket #', i, '  --- E M P T Y --- '
    print 'RDV STORE: ', rdvStore
    print '\n --  --  --  --  -- --  --  --  --  -- --  --  --  --  -- \n'
    time.sleep(ROUND_TIME)
